package process

import (
	"fmt"
	"log"
	"sync"
	"time"

	"gitee.com/nggs/protoactor-go/actor"
	"gitee.com/nggs/protoactor-go/remote"

	myactor "gitee.com/nggs/actor"
	mydebug "gitee.com/nggs/debug"
	mylog "gitee.com/nggs/log"
	"gitee.com/nggs/util"

	"gitee.com/nggs/microservice/export"
	msservice "gitee.com/nggs/microservice/service"
)

type ID = int

type Super struct {
	myactor.ISuper

	startedWg sync.WaitGroup
	stoppedWg sync.WaitGroup

	cfg    Config
	logger mylog.ILogger

	pprofAddr string

	services []export.IService
}

func NewSuper(cfg Config) (s *Super) {
	s = &Super{
		cfg:    cfg,
		logger: mylog.NewConsoleLogger(),
	}

	s.ISuper = myactor.NewSuper(nil, &s.startedWg, &s.stoppedWg).
		WithOnStartedHandler(s.onStarted).
		//WithOnStoppingHandler(s.onStopping).
		WithOnStoppedHandler(s.onStopped).
		//WithOnRestartingHandler(s.onRestarting).
		//WithOnRestartingHandler(s.onRestarted).
		WithOnActorTerminateHandler(s.onActorTerminated)

	for _, cfg := range s.cfg.Services {
		svc, err := msservice.FM.Produce(cfg.Name)
		if err != nil {
			log.Panicf("produce servcie[%s] fail, %s", cfg.Name, err)
		}
		err = svc.Init(cfg.ID, &s.startedWg, &s.stoppedWg, s.cfg)
		if err != nil {
			log.Panicf("init %s[%d] fail, %s", cfg.Name, cfg.ID, err)
		}
		s.services = append(s.services, svc)
	}

	return
}

//func (s Super) ID() ID {
//	return s.cfg.ID()
//}
//
//func (s Super) Config() Config {
//	return s.cfg
//}

func (s *Super) Run() (err error) {
	if s.cfg.PprofIP != "" {
		// 开启pprof server
		s.pprofAddr, err = mydebug.StartPprofServer(s.cfg.PprofIP, s.cfg.PprofPort)
		if err != nil {
			panic(err)
		}
		defer mydebug.StopPprofServer()
		s.Info("start pprof server in %s", s.pprofAddr)
	}

	if s.cfg.EndpointIP != "" {
		remote.Start(fmt.Sprintf("%s:%d", s.cfg.EndpointIP, s.cfg.EndpointPort))
		s.Info("start remote in %s", actor.ProcessRegistry.Address)
	}

	err = s.Start(nil, fmt.Sprintf("p-%d", s.cfg.ID()))
	if err != nil {
		panic(err)
	}

	// 启动接受ctrl+c命令的协程
	go func() {
		util.WaitExitSignal()
		s.Info("receive exit signal")

		for i := len(s.services) - 1; i >= 0; i-- {
			svc := s.services[i]
			svc.Stop()
			if s.cfg.StopServiceIntervalMillisecond > 0 {
				time.Sleep(s.cfg.StopServiceIntervalMillisecond * time.Millisecond)
			}
		}

		if s.cfg.ExitWaitSecond > 0 {
			time.Sleep(s.cfg.ExitWaitSecond * time.Second)
		}

		s.Stop()
	}()

	s.WaitForStarted()

	s.WaitForStopped()

	return
}

func (s *Super) onStarted(ctx actor.Context) {
	for i, svc := range s.services {
		svcCfg := s.cfg.Services[i]
		svcName := fmt.Sprintf("%s-%d", svcCfg.Name, svcCfg.ID)
		err := svc.Run(ctx, s.pprofAddr)
		if err != nil {
			s.Error("start %s fail, %v", svcName, err)
			s.Stop()
			return
		}
		s.Info("start %s success", svcName)
		if s.cfg.StartServiceIntervalMillisecond > 0 {
			time.Sleep(s.cfg.StartServiceIntervalMillisecond * time.Millisecond)
		}
	}
}

//func (s *Super) onStopping(ctx actor.Context) {
//
//}

func (s *Super) onStopped(ctx actor.Context) {
	s.Info("stopped")
}

func (s *Super) onActorTerminated(who *actor.PID, ctx actor.Context) {
	s.Info("%s terminated", who.String())
}

func (s *Super) onRestarting(ctx actor.Context) {

}

func (s *Super) onRestarted(ctx actor.Context) {

}
